// SPDX-License-Identifier: GPL-2.0
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/pci.h>
#include <linux/sched.h>
#include <linux/semaphore.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/version.h>
#include <linux/wait.h>
#include <linux/timer.h>
#include <linux/clk.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/time.h>
#include <linux/delay.h>
#include <mach/debug.h>
#include "vdc_driver.h"
#include "dwl_defs.h"

#define NEED
#define DEVDRV_NAME_VDC			"vdc"
#define VDC_DRV_MINOR			81

#define HXDEC_MAX_CORES			4

/* hantro G1 regs config including dec and pp */
#define HANTRO_DEC_REGS			60
#define HANTRO_PP_REGS			41

#define HANTRO_DEC_EXT_REGS		27
#define HANTRO_PP_EXT_REGS		9

#define HANTRO_G1_DEC_TOTAL_REGS	(HANTRO_DEC_REGS + HANTRO_DEC_EXT_REGS)
#define HANTRO_PP_TOTAL_REGS		(HANTRO_PP_REGS + HANTRO_PP_EXT_REGS)

#define G1_D_REGS		155 /*G1 total regs*/

#define HANTRO_DEC_ORG_FIRST_REG	0
#define HANTRO_DEC_ORG_LAST_REG		59
#define HANTRO_DEC_EXT_FIRST_REG	119
#define HANTRO_DEC_EXT_LAST_REG		145

#define HANTRO_PP_ORG_FIRST_REG		60
#define HANTRO_PP_ORG_LAST_REG		100
#define HANTRO_PP_EXT_FIRST_REG		146
#define HANTRO_PP_EXT_LAST_REG		154

/* hantro G2 reg config */
#define G2_D_REGS			337 /*G2 total regs*/
#define HANTRO_G2_DEC_FIRST_REG		0
#define HANTRO_G2_DEC_LAST_REG		(G2_D_REGS - 1)

/* hantro VC8000D reg config */
#define VC8000D_REGS			437 /*VC8000D total regs*/
#define HANTRO_VC8000D_FIRST_REG	0
#define HANTRO_VC8000D_LAST_REG		(VC8000D_REGS - 1)

/* Logic module IRQs */
#define HXDEC_NO_IRQ			-1

#define MAX(a, b)			(((a) > (b)) ? (a) : (b))

#define DEC_IO_SIZE_MAX (MAX(MAX(G2_D_REGS, G1_D_REGS), VC8000D_REGS) * 4)

/* User should modify these configuration if do porting to own platform. */
/* Please guarantee the base_addr, io_size, dec_irq belong to same core. */

/* Defines use kernel clk cfg or not**/
#define CLK_CFG
#ifdef CLK_CFG
static struct clk *clk_cfg;
static struct clk *parent_pll;
static struct clk *vdc_ahb_gate;
static struct clk *vdc_ahb_reset;
static struct clk *vdc_axi_gate;
int is_clk_on;
#define VDC_CLK_RATE		300000000
#endif

#define IS_G1(hw_id)			(((hw_id) == 0x6731) ? 1 : 0)
#define IS_G2(hw_id)			(((hw_id) == 0x6732) ? 1 : 0)
#define IS_VC8000D(hw_id)		(((hw_id) == 0x8001) ? 1 : 0)

static const int DecHwId[] = {
	0x6731, /* G1 */
	0x6732, /* G2 */
	0x8001  /* VC8000D */
};

/* Logic module base address */
#define SOCLE_LOGIC_0_BASE		0x01380000
#define DEC_IO_SIZE_0			DEC_IO_SIZE_MAX /* bytes */
#define DEC_IRQ_0			91

unsigned long multicorebase[HXDEC_MAX_CORES] = {
	SOCLE_LOGIC_0_BASE,
	-1,
	-1,
	-1
};

int irq[HXDEC_MAX_CORES] = {
	DEC_IRQ_0,
	-1,
	-1,
	-1
};

unsigned int iosize[HXDEC_MAX_CORES] = {
	DEC_IO_SIZE_0,
	-1,
	-1,
	-1
};

/* Because one core may contain multi-pipeline, multicore base may be changed */
unsigned long multicorebase_actual[HXDEC_MAX_CORES];

static int vdc_is_idle = 1;
int elements = 1;

static const struct of_device_id vdc_of_match[] = {
	{.compatible = "lombo,n9-vdc",},
	{}
};


/* here's all the must remember stuff */
typedef struct {
	char *buffer;
	unsigned int iosize[HXDEC_MAX_CORES];
	u8 *hwregs[HXDEC_MAX_CORES];
	int irq[HXDEC_MAX_CORES];
	int hw_id[HXDEC_MAX_CORES];
	int cores;
	struct fasync_struct *async_queue_dec;
	struct fasync_struct *async_queue_pp;
} hantrodec_t;


typedef struct {
	u32 cfg[HXDEC_MAX_CORES];              /* indicate supported format */
	u32 cfg_backup[HXDEC_MAX_CORES];       /* back up of cfg */
	int its_main_core_id[HXDEC_MAX_CORES]; /* indicate if main core exist */
	int its_aux_core_id[HXDEC_MAX_CORES];  /* indicate if aux core exist */
} core_cfg;

static hantrodec_t hantrodec_data; /* dynamic allocation? */

static int ReserveIO(struct platform_device *pdev);
static void ReleaseIO(void);

static void ResetAsic(hantrodec_t *dev);

#ifdef HANTRODEC_DEBUG
static void dump_regs(hantrodec_t *dev);
#endif

/* IRQ handler */

static irqreturn_t hantrodec_isr(int irq, void *dev_id);


static u32 dec_regs[HXDEC_MAX_CORES][DEC_IO_SIZE_MAX / 4];
struct semaphore dec_core_sem;
struct semaphore pp_core_sem;

static int dec_irq = -1;
static int pp_irq = -1;

atomic_t irq_rx = ATOMIC_INIT(0);
atomic_t irq_tx = ATOMIC_INIT(0);

static struct file *dec_owner[HXDEC_MAX_CORES];
static struct file *pp_owner[HXDEC_MAX_CORES];
static int CoreHasFormat(const u32 *cfg, int core, u32 format);

/* spinlock_t owner_lock = SPIN_LOCK_UNLOCKED; */
DEFINE_SPINLOCK(owner_lock);

DECLARE_WAIT_QUEUE_HEAD(dec_wait_queue);
DECLARE_WAIT_QUEUE_HEAD(pp_wait_queue);
DECLARE_WAIT_QUEUE_HEAD(hw_queue);
#ifdef CLK_CFG
DEFINE_SPINLOCK(clk_lock);
#endif

#define DWL_CLIENT_TYPE_H264_DEC        1U
#define DWL_CLIENT_TYPE_MPEG4_DEC       2U
#define DWL_CLIENT_TYPE_JPEG_DEC        3U
#define DWL_CLIENT_TYPE_PP              4U
#define DWL_CLIENT_TYPE_VC1_DEC         5U
#define DWL_CLIENT_TYPE_MPEG2_DEC       6U
#define DWL_CLIENT_TYPE_VP6_DEC         7U
#define DWL_CLIENT_TYPE_AVS_DEC         8U
#define DWL_CLIENT_TYPE_RV_DEC          9U
#define DWL_CLIENT_TYPE_VP8_DEC         10U
#define DWL_CLIENT_TYPE_VP9_DEC         11U
#define DWL_CLIENT_TYPE_HEVC_DEC        12U

static core_cfg config;

static void ReadCoreConfig(hantrodec_t *dev)
{
	int c;
	u32 reg, tmp, mask;

	memset(config.cfg, 0, sizeof(config.cfg));

for (c = 0; c < dev->cores; c++) {
	/* Decoder configuration */
	if (IS_G1(dev->hw_id[c])) {
		reg = ioread32((void *)(dev->hwregs[c] +
			 HANTRODEC_SYNTH_CFG * 4));

		tmp = (reg >> DWL_H264_E) & 0x3U;
		if (tmp)
			PRT_ERR("hantrodec: core[%d] has H264\n", c);
		config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_H264_DEC : 0;

		tmp = (reg >> DWL_JPEG_E) & 0x01U;
		if (tmp)
			PRT_ERR("hantrodec: core[%d] has JPEG\n", c);
		config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_JPEG_DEC : 0;

		tmp = (reg >> DWL_HJPEG_E) & 0x01U;
		if (tmp)
			PRT_ERR("hantrodec: core[%d] has HJPEG\n", c);
		config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_JPEG_DEC : 0;

		tmp = (reg >> DWL_MPEG4_E) & 0x3U;
		if (tmp)
			PRT_ERR("hantrodec: core[%d] has MPEG4\n", c);
		config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_MPEG4_DEC : 0;

		tmp = (reg >> DWL_VC1_E) & 0x3U;
		if (tmp)
			PRT_ERR("hantrodec: core[%d] has VC1\n", c);
		config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_VC1_DEC : 0;

		tmp = (reg >> DWL_MPEG2_E) & 0x01U;
		if (tmp)
			PRT_ERR("hantrodec: core[%d] has MPEG2\n", c);
		config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_MPEG2_DEC : 0;

		tmp = (reg >> DWL_VP6_E) & 0x01U;
		if (tmp)
			PRT_ERR("hantrodec: core[%d] has VP6\n", c);
		config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_VP6_DEC : 0;

		reg = ioread32((void *)(dev->hwregs[c] +
				HANTRODEC_SYNTH_CFG_2 * 4));

		/* VP7 and WEBP is part of VP8 */
		mask = (1 << DWL_VP8_E) | (1 << DWL_VP7_E) | (1 << DWL_WEBP_E);
		tmp = (reg & mask);
		if (tmp & (1 << DWL_VP8_E))
			PRT_ERR("hantrodec: core[%d] has VP8\n", c);
		if (tmp & (1 << DWL_VP7_E))
			PRT_ERR("hantrodec: core[%d] has VP7\n", c);
		if (tmp & (1 << DWL_WEBP_E))
			PRT_ERR("hantrodec: core[%d] has WebP\n", c);
		config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_VP8_DEC : 0;

		tmp = (reg >> DWL_AVS_E) & 0x01U;
		if (tmp)
			PRT_ERR("hantrodec: core[%d] has AVS\n", c);
		config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_AVS_DEC : 0;

		tmp = (reg >> DWL_RV_E) & 0x03U;
		if (tmp)
			PRT_ERR("hantrodec: core[%d] has RV\n", c);
		config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_RV_DEC : 0;

		/* Post-processor configuration */
		reg = ioread32((void *)(dev->hwregs[c] +
					HANTROPP_SYNTH_CFG * 4));

		tmp = (reg >> DWL_G1_PP_E) & 0x01U;
		if (tmp)
			PRT_ERR("hantrodec: core[%d] has PP\n", c);
		config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_PP : 0;
	} else if ((IS_G2(dev->hw_id[c]))) {
		reg = ioread32((void *)(dev->hwregs[c] +
					HANTRODEC_CFG_STAT * 4));

		tmp = (reg >> DWL_G2_HEVC_E) & 0x01U;
		if (tmp)
			PRT_ERR("hantrodec: core[%d] has HEVC\n", c);
		config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_HEVC_DEC : 0;

		tmp = (reg >> DWL_G2_VP9_E) & 0x01U;
		if (tmp)
			PRT_ERR("hantrodec: core[%d] has VP9\n", c);
		config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_VP9_DEC : 0;

		/* Post-processor configuration */
		reg = ioread32((void *)(dev->hwregs[c] +
					HANTRODECPP_SYNTH_CFG * 4));

		tmp = (reg >> DWL_G2_PP_E) & 0x01U;
		if (tmp)
			PRT_ERR("hantrodec: core[%d] has PP\n", c);
		config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_PP : 0;
	} else if ((IS_VC8000D(dev->hw_id[c])) &&
			config.its_main_core_id[c] < 0) {
		reg = ioread32((void *)(dev->hwregs[c] +
					HANTRODEC_SYNTH_CFG * 4));

		tmp = (reg >> DWL_H264_E) & 0x3U;
		if (tmp)
			PRT_ERR("hantrodec: core[%d] has H264\n", c);
		config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_H264_DEC : 0;

		tmp = (reg >> DWL_H264HIGH10_E) & 0x01U;
		if (tmp)
			PRT_ERR("hantrodec: core[%d] has H264HIGH10\n", c);
		config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_H264_DEC : 0;

		tmp = (reg >> DWL_JPEG_E) & 0x01U;
		if (tmp)
			PRT_ERR("hantrodec: core[%d] has JPEG\n", c);
		config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_JPEG_DEC : 0;

		tmp = (reg >> DWL_HJPEG_E) & 0x01U;
		if (tmp)
			PRT_ERR("hantrodec: core[%d] has HJPEG\n", c);
		config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_JPEG_DEC : 0;

		tmp = (reg >> DWL_MPEG4_E) & 0x3U;
		if (tmp)
			PRT_ERR("hantrodec: core[%d] has MPEG4\n", c);
		config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_MPEG4_DEC : 0;

		tmp = (reg >> DWL_VC1_E) & 0x3U;
		if (tmp)
			PRT_ERR("hantrodec: core[%d] has VC1\n", c);
		config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_VC1_DEC : 0;

		tmp = (reg >> DWL_MPEG2_E) & 0x01U;
		if (tmp)
			PRT_ERR("hantrodec: core[%d] has MPEG2\n", c);
		config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_MPEG2_DEC : 0;

		tmp = (reg >> DWL_VP6_E) & 0x01U;
		if (tmp)
			PRT_ERR("hantrodec: core[%d] has VP6\n", c);
		config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_VP6_DEC : 0;

		reg = ioread32((void *)(dev->hwregs[c] +
					HANTRODEC_SYNTH_CFG_2 * 4));

		/* VP7 and WEBP is part of VP8 */
		mask = (1 << DWL_VP8_E) | (1 << DWL_VP7_E) | (1 << DWL_WEBP_E);
		tmp = (reg & mask);
		if (tmp & (1 << DWL_VP8_E))
			PRT_ERR("hantrodec: core[%d] has VP8\n", c);
		if (tmp & (1 << DWL_VP7_E))
			PRT_ERR("hantrodec: core[%d] has VP7\n", c);
		if (tmp & (1 << DWL_WEBP_E))
			PRT_ERR("hantrodec: core[%d] has WebP\n", c);
		config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_VP8_DEC : 0;

		tmp = (reg >> DWL_AVS_E) & 0x01U;
		if (tmp)
			PRT_ERR("hantrodec: core[%d] has AVS\n", c);
		config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_AVS_DEC : 0;

		tmp = (reg >> DWL_RV_E) & 0x03U;
		if (tmp)
			PRT_ERR("hantrodec: core[%d] has RV\n", c);
		config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_RV_DEC : 0;

		reg = ioread32((void *)(dev->hwregs[c] +
					HANTRODEC_SYNTH_CFG_3 * 4));

		tmp = (reg >> DWL_HEVC_E) & 0x07U;
		if (tmp)
			PRT_ERR("hantrodec: core[%d] has HEVC\n", c);
		config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_HEVC_DEC : 0;

		tmp = (reg >> DWL_VP9_E) & 0x07U;
		if (tmp)
			PRT_ERR("hantrodec: core[%d] has VP9\n", c);
		config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_VP9_DEC : 0;

		/* Post-processor configuration */
		reg = ioread32((void *)(dev->hwregs[c] +
					HANTRODECPP_CFG_STAT * 4));

		tmp = (reg >> DWL_PP_E) & 0x01U;
		if (tmp)
			PRT_ERR("hantrodec: core[%d] has PP\n", c);
		config.cfg[c] |= tmp ? 1 << DWL_CLIENT_TYPE_PP : 0;

		if (config.its_aux_core_id[c] >= 0) {
			/* set main_core_id and aux_core_id */
			reg = ioread32((void *)(dev->hwregs[c] +
					HANTRODEC_SYNTH_CFG_2 * 4));

			tmp = (reg >> DWL_H264_PIPELINE_E) & 0x01U;
			if (tmp)
				PRT_ERR("hantrode:core[%d] pipeline H264\n", c);
			config.cfg[config.its_aux_core_id[c]] |= tmp ?
					1 << DWL_CLIENT_TYPE_H264_DEC : 0;

			tmp = (reg >> DWL_JPEG_PIPELINE_E) & 0x01U;
			if (tmp)
				PRT_ERR("hantrodec:core[%d] pipeline JPE\n", c);
			config.cfg[config.its_aux_core_id[c]] |= tmp ?
					1 << DWL_CLIENT_TYPE_JPEG_DEC : 0;
		}
	}
}
	memcpy(config.cfg_backup, config.cfg, sizeof(config.cfg));
}

static int CoreHasFormat(const u32 *cfg, int core, u32 format)
{
	return (cfg[core] & (1 << format)) ? 1 : 0;
}

int GetDecCore(long core, hantrodec_t *dev,
		struct file *filp, unsigned long format)
{
	int success = 0;
	unsigned long flags;

	spin_lock_irqsave(&owner_lock, flags);
	if (CoreHasFormat(config.cfg, core, format) && dec_owner[core]
			== NULL /*&& config.its_main_core_id[core] >= 0*/) {
		dec_owner[core] = filp;
		success = 1;

/*
 *If one main core takes one format which doesn't supported by aux core,
 *set aux core's cfg to none video format support
 */
		if (config.its_aux_core_id[core] >= 0 && !CoreHasFormat(
			config.cfg, config.its_aux_core_id[core], format)) {
			config.cfg[config.its_aux_core_id[core]] = 0;
		}
/*
 *If one aux core takes one format, set main core's
 *	cfg to aux core supported video format
 */
		else if (config.its_main_core_id[core] >= 0) {
			config.cfg[config.its_main_core_id[core]] =
							config.cfg[core];
		}
	}

	spin_unlock_irqrestore(&owner_lock, flags);

	return success;
}

int GetDecCoreAny(long *core, hantrodec_t *dev, struct file *filp,
	unsigned long format)
{
	int success = 0;
	long c;

	*core = -1;

	for (c = 0; c < dev->cores; c++) {
		/* a free core that has format */
		if (GetDecCore(c, dev, filp, format)) {
			success = 1;
			*core = c;
			break;
		}
	}

	return success;
}

int GetDecCoreID(hantrodec_t *dev, struct file *filp,
	unsigned long format)
{
	long c;
	unsigned long flags;

	int core_id = -1;

	for (c = 0; c < dev->cores; c++) {
		/* a core that has format */
		spin_lock_irqsave(&owner_lock, flags);
		if (CoreHasFormat(config.cfg, c, format)) {
			core_id = c;
			spin_unlock_irqrestore(&owner_lock, flags);
			break;
		}
		spin_unlock_irqrestore(&owner_lock, flags);
	}
	return core_id;
}

#ifdef NOT_NEED
static int hantrodec_choose_core(int is_g1)
{
	unsigned char *reg = NULL;
	unsigned int blk_base = 0x38320000;

	PRT_ERR("%s\n", __func__);
	if (!request_mem_region(blk_base, 0x1000, "blk_ctl")) {
		PRT_ERR("blk_ctl: failed to reserve HW regs\n");
		return -EBUSY;
	}

	reg = (u8 *) ioremap_nocache(blk_base, 0x1000);

	if (reg == NULL) {
		PRT_ERR("blk_ctl: failed to ioremap HW regs\n");
		if (reg)
			iounmap((void *)reg);
		release_mem_region(blk_base, 0x1000);
		return -EBUSY;
	}

/*
 *	G1 use, set to 1; G2 use, set to 0, choose the one you are using
 *	user should modify the reg according to platform design
 */
	if (is_g1)
		iowrite32(0x1, (void *)(reg + 0x14)); // VPUMIX only use G1,
	else
		iowrite32(0x0, (void *)(reg + 0x14)); // VPUMIX only use G2,

	if (reg)
		iounmap((void *)reg);
	release_mem_region(blk_base, 0x1000);
	PRT_ERR("%s OK!\n", __func__);
	return 0;
}
#endif

long ReserveDecoder(hantrodec_t *dev, struct file *filp, unsigned long format)
{
	long core = -1;

	/* reserve a core */
	if (down_interruptible(&dec_core_sem))
		return -ERESTARTSYS;

	/* lock a core that has specific format*/
	if (wait_event_interruptible(hw_queue,
			GetDecCoreAny(&core, dev, filp, format) != 0))
		return -ERESTARTSYS;

#ifdef NOT_NEED
	if (IS_G1(dev->hw_id[core])) {
		if (hantrodec_choose_core(1) == 0)
			PRT_ERR("G1 is reserved\n");
		else
			return -1;
	} else {
		if (hantrodec_choose_core(0) == 0)
			PRT_ERR("G2 is reserved\n");
		else
			return -1;
	}
#endif

	return core;
}

void ReleaseDecoder(hantrodec_t *dev, long core)
{
	u32 status;
	unsigned long flags;

	status = ioread32((void *)(dev->hwregs[core] +
				HANTRODEC_IRQ_STAT_DEC_OFF));

	/* make sure HW is disabled */
	if (status & HANTRODEC_DEC_E) {
		PRT_ERR("hantrodec: DEC[%li] still enabled -> reset\n", core);

		/* abort decoder */
		status |= HANTRODEC_DEC_ABORT | HANTRODEC_DEC_IRQ_DISABLE;
		iowrite32(status, (void *)(dev->hwregs[core] +
					HANTRODEC_IRQ_STAT_DEC_OFF));
	}

	spin_lock_irqsave(&owner_lock, flags);

	/* If aux core released, revert main core's config back */
	if (config.its_main_core_id[core] >= 0) {
		config.cfg[config.its_main_core_id[core]] =
			config.cfg_backup[config.its_main_core_id[core]];
	}

	/* If main core released, revert aux core's config back */
	if (config.its_aux_core_id[core] >= 0) {
		config.cfg[config.its_aux_core_id[core]] =
			config.cfg_backup[config.its_aux_core_id[core]];
	}

	dec_owner[core] = NULL;

	spin_unlock_irqrestore(&owner_lock, flags);

	up(&dec_core_sem);

	wake_up_interruptible_all(&hw_queue);
}

long ReservePostProcessor(hantrodec_t *dev, struct file *filp)
{
	unsigned long flags;

	long core = 0;

	/* single core PP only */
	if (down_interruptible(&pp_core_sem))
		return -ERESTARTSYS;

	spin_lock_irqsave(&owner_lock, flags);

	pp_owner[core] = filp;

	spin_unlock_irqrestore(&owner_lock, flags);

	return core;
}

void ReleasePostProcessor(hantrodec_t *dev, long core)
{
	unsigned long flags;

	u32 status = ioread32((void *)(dev->hwregs[core] +
					HANTRO_IRQ_STAT_PP_OFF));

	/* make sure HW is disabled */
	if (status & HANTRO_PP_E) {
		PRT_ERR("hantrodec: PP[%li] still enabled -> reset\n", core);

		/* disable IRQ */
		status |= HANTRO_PP_IRQ_DISABLE;

		/* disable postprocessor */
		status &= (~HANTRO_PP_E);
		iowrite32(0x10, (void *)(dev->hwregs[core] +
					HANTRO_IRQ_STAT_PP_OFF));
	}

	spin_lock_irqsave(&owner_lock, flags);

	pp_owner[core] = NULL;

	spin_unlock_irqrestore(&owner_lock, flags);

	up(&pp_core_sem);
}

long ReserveDecPp(hantrodec_t *dev, struct file *filp, unsigned long format)
{
	/* reserve core 0, DEC+PP for pipeline */
	unsigned long flags;

	long core = 0;

	/* check that core has the requested dec format */
	if (!CoreHasFormat(config.cfg, core, format))
		return -EFAULT;

	/* check that core has PP */
	if (!CoreHasFormat(config.cfg, core, DWL_CLIENT_TYPE_PP))
		return -EFAULT;

	/* reserve a core */
	if (down_interruptible(&dec_core_sem))
		return -ERESTARTSYS;

	/* wait until the core is available */
	if (wait_event_interruptible(hw_queue,
			GetDecCore(core, dev, filp, format) != 0)) {
		up(&dec_core_sem);
		return -ERESTARTSYS;
	}

	if (down_interruptible(&pp_core_sem)) {
		ReleaseDecoder(dev, core);
		return -ERESTARTSYS;
	}

	spin_lock_irqsave(&owner_lock, flags);
	pp_owner[core] = filp;
	spin_unlock_irqrestore(&owner_lock, flags);

	return core;
}

long DecFlushRegs(hantrodec_t *dev, struct core_desc *core)
{
	long ret = 0, i;

	u32 id = core->id;

	vdc_is_idle = 0;
#ifdef NEED
	ret = copy_from_user(dec_regs[id], core->regs, VC8000D_REGS * 4);
	if (ret) {
		PRT_ERR("copy_from_user failed, returned %li\n", ret);
		return -EFAULT;
	}

	/* write all regs but the status reg[1] to hardware */
	iowrite32(0x0, (void *)(dev->hwregs[id] + 4));
	for (i = 2; i <= HANTRO_VC8000D_LAST_REG; i++)
		iowrite32(dec_regs[id][i], (void *)(dev->hwregs[id] + i * 4));

#else
	if (IS_G1(dev->hw_id[id])) {
		/* copy original dec regs to kernal space*/
		ret = copy_from_user(dec_regs[id], core->regs,
						HANTRO_DEC_REGS * 4);
		if (ret) {
			PRT_ERR("copy_from_user failed, returned %li\n", ret);
			return -EFAULT;
		}
#ifdef USE_64BIT_ENV
		/* copy extended dec regs to kernal space*/
		ret = copy_from_user(dec_regs[id] + HANTRO_DEC_EXT_FIRST_REG,
				core->regs + HANTRO_DEC_EXT_FIRST_REG,
				HANTRO_DEC_EXT_REGS * 4);
#endif
		if (ret) {
			PRT_ERR("copy_from_user failed, returned %li\n", ret);
			return -EFAULT;
		}

		/* write dec regs but the status reg[1] to hardware */
		/* both original and extended regs need to be written */
		for (i = 2; i <= HANTRO_DEC_ORG_LAST_REG; i++)
			iowrite32(dec_regs[id][i], dev->hwregs[id] + i * 4);

#ifdef USE_64BIT_ENV
		for (i = HANTRO_DEC_EXT_FIRST_REG;
				i <= HANTRO_DEC_EXT_LAST_REG; i++)
			iowrite32(dec_regs[id][i], dev->hwregs[id] + i * 4);
#endif
	} else {
		ret = copy_from_user(dec_regs[id],
				core->regs, G2_D_REGS * 4);
		if (ret) {
			PRT_ERR("copy_from_user failed, returned %li\n", ret);
			return -EFAULT;
		}

		/* write all regs but the status reg[1] to hardware */
		for (i = 2; i <= HANTRO_G2_DEC_LAST_REG; i++) {
#ifdef NOT_NEED
			if (i == 2)
				PRT_ERR("reg2=%08x\n", dec_regs[id][i]);
				//dec_regs[id][i] = 0x78777777;
				//dec_regs[id][i] = 0x78778787;
				//c_regs[id][i] = 0x78888888; //64bit
				//dec_regs[id][i] = 0xF0F00000;
				//128bit 0xF0F0F000

			//dec_regs[id][i] = 0xF0F0000F;
			//128bit 0xF0F00000 for big endian
			if (i == 58)
				PRT_ERR("reg58=%08x\n", dec_regs[id][i]);
				//dec_regs[id][i]= 0x210;//128bit
				//dec_regs[id][i]= 0x110;//64bit

			if (i == 3)
				PRT_ERR("reg3=%08x\n", dec_regs[id][i]);
				//dec_regs[id][i] |= 0x00F00000;//128bit
				//dec_regs[id][i]= 0x110;//64bit

#endif
			iowrite32(dec_regs[id][i], dev->hwregs[id] + i * 4);

		}
	}
#endif

	/* write the status register, which may start the decoder */
	iowrite32(dec_regs[id][1], (void *)(dev->hwregs[id] + 4));

	PRT_ERR("flushed registers on core %d\n", id);

	return 0;
}


long DecWriteRegs(hantrodec_t *dev, struct core_desc *core)
{
	long ret = 0, i;
	u32 id = core->id;

	i = core->reg_id;
	ret = copy_from_user(dec_regs[id] + core->reg_id,
				core->regs + core->reg_id, 4);

	if (ret) {
		PRT_ERR("copy_from_user failed, returned %li\n", ret);
		return -EFAULT;
	}

	iowrite32(dec_regs[id][i], (void *)(dev->hwregs[id] + i * 4));
	return 0;
}

long DecReadRegs(hantrodec_t *dev, struct core_desc *core)
{
	long ret, i;
	u32 id = core->id;

	i = core->reg_id;
	/* user has to know exactly what they are asking for */
	//if(core->size != (VC8000D_REGS * 4))
	//  return -EFAULT;

	/* read specific registers from hardware */
	i = core->reg_id;
	dec_regs[id][i] = ioread32((void *)(dev->hwregs[id] + i * 4));

	/* put registers to user space*/
	ret = copy_to_user(core->regs + core->reg_id,
				dec_regs[id] + core->reg_id, 4);
	if (ret) {
		PRT_ERR("copy_to_user failed, returned %li\n", ret);
		return -EFAULT;
	}
	return 0;
}

long DecRefreshRegs(hantrodec_t *dev, struct core_desc *core)
{
	long ret, i;

	u32 id = core->id;
#ifdef NEED
	//for(i = 0; i <= HANTRO_DEC_ORG_LAST_REG; i++) {
	for (i = 0; i <= HANTRO_VC8000D_LAST_REG; i++)
		dec_regs[id][i] = ioread32((void *)(dev->hwregs[id] + i * 4));

	//ret = copy_to_user(core->regs, dec_regs[id], HANTRO_DEC_REGS*4);
	ret = copy_to_user(core->regs, dec_regs[id], G2_D_REGS * 4);
	if (ret) {
		PRT_ERR("copy_to_user failed, returned %li\n", ret);
		return -EFAULT;
	}
#else

	if (IS_G1(dev->hw_id[id])) {
#ifdef USE_64BIT_ENV
		/* user has to know exactly what they are asking for */
		if (core->size != (HANTRO_DEC_TOTAL_REGS * 4))
			return -EFAULT;
#else
		/* user has to know exactly what they are asking for */
		//if(core->size != (HANTRO_DEC_REGS * 4))
		//  return -EFAULT;
#endif
		/* read all registers from hardware */
		/* both original and extended regs need to be read */
		for (i = 0; i <= HANTRO_DEC_ORG_LAST_REG; i++)
			dec_regs[id][i] = ioread32(dev->hwregs[id] + i * 4);
#ifdef USE_64BIT_ENV
		for (i = HANTRO_DEC_EXT_FIRST_REG;
					i <= HANTRO_DEC_EXT_LAST_REG; i++)
			dec_regs[id][i] = ioread32(dev->hwregs[id] + i * 4);
#endif
		/* put registers to user space*/
		/* put original registers to user space*/
		ret = copy_to_user(core->regs, dec_regs[id],
						HANTRO_DEC_REGS * 4);
#ifdef USE_64BIT_ENV
		/*put extended registers to user space*/
		ret = copy_to_user(core->regs + HANTRO_DEC_EXT_FIRST_REG,
				dec_regs[id] + HANTRO_DEC_EXT_FIRST_REG,
				HANTRO_DEC_EXT_REGS * 4);
#endif
		if (ret) {
			PRT_ERR("copy_to_user failed, returned %li\n", ret);
			return -EFAULT;
		}
	} else {
		/* user has to know exactly what they are asking for */
		if (core->size != (G2_D_REGS * 4))
			return -EFAULT;

		/* read all registers from hardware */
		for (i = 0; i <= HANTRO_G2_DEC_LAST_REG; i++)
			dec_regs[id][i] = ioread32(dev->hwregs[id] + i * 4);

		/* put registers to user space*/
		ret = copy_to_user(core->regs,
			dec_regs[id], G2_D_REGS * 4);
		if (ret) {
			PRT_ERR("copy_to_user failed, returned %li\n", ret);
			return -EFAULT;
		}
	}
#endif
	return 0;
}

static int CheckDecIrq(hantrodec_t *dev, int id)
{
	unsigned long flags;
	int rdy = 0;

	const u32 irq_mask = (1 << id);

	spin_lock_irqsave(&owner_lock, flags);

	if (dec_irq & irq_mask) {
		/* reset the wait condition(s) */
		dec_irq &= ~irq_mask;
		rdy = 1;
	}

	spin_unlock_irqrestore(&owner_lock, flags);

	return rdy;
}

long WaitDecReadyAndRefreshRegs(hantrodec_t *dev, struct core_desc *core)
{
	u32 id = core->id;
	long ret;

	PRT_ERR("wait_event_interruptible DEC[%d]\n", id);
#ifdef USE_SW_TIMEOUT
	u32 status;

	ret = wait_event_interruptible_timeout(dec_wait_queue,
			CheckDecIrq(dev, id), msecs_to_jiffies(2000));
	if (ret < 0) {
		PRT_ERR("DEC[%d]  wait_event_interruptible interrupted\n", id);
		return -ERESTARTSYS;
	} else if (ret == 0) {
		PRT_ERR("DEC[%d]  wait_event_interruptible timeout\n", id);
		status = ioread32((void *)(dev->hwregs[id] +
					HANTRODEC_IRQ_STAT_DEC_OFF));
		/* check if HW is enabled */
		if (status & HANTRODEC_DEC_E) {
			PRT_ERR("hantrodec: DEC[%d] reset for timeout\n", id);

			/* abort decoder */
			status |= HANTRODEC_DEC_ABORT |
					HANTRODEC_DEC_IRQ_DISABLE;
			iowrite32(status, (void *)(dev->hwregs[id] +
						HANTRODEC_IRQ_STAT_DEC_OFF));
		}
	}
#else
	ret = wait_event_interruptible(dec_wait_queue, CheckDecIrq(dev, id));
	if (ret) {
		PRT_ERR("DEC[%d]  wait_event_interruptible interrupted\n", id);
		return -ERESTARTSYS;
	}
#endif
	atomic_inc(&irq_tx);
	vdc_is_idle = 1;
	/* refresh registers */
	return DecRefreshRegs(dev, core);
}

long PPFlushRegs(hantrodec_t *dev, struct core_desc *core)
{
	long ret = 0;
	u32 id = core->id;
	u32 i;

	/* copy original dec regs to kernal space*/
	ret = copy_from_user(dec_regs[id] + HANTRO_PP_ORG_FIRST_REG,
			core->regs + HANTRO_PP_ORG_FIRST_REG,
			HANTRO_PP_REGS * 4);
	if (sizeof(void *) == 8) {
		/* copy extended dec regs to kernal space*/
		ret = copy_from_user(dec_regs[id] + HANTRO_PP_EXT_FIRST_REG,
				core->regs + HANTRO_PP_EXT_FIRST_REG,
				HANTRO_PP_EXT_REGS * 4);
	}
	if (ret) {
		PRT_ERR("copy_from_user failed, returned %li\n", ret);
		return -EFAULT;
	}

	/* write all regs but the status reg[1] to hardware */
	/* both original and extended regs need to be written */
	for (i = HANTRO_PP_ORG_FIRST_REG + 1; i <= HANTRO_PP_ORG_LAST_REG; i++)
		iowrite32(dec_regs[id][i], (void *)(dev->hwregs[id] + i * 4));
	if (sizeof(void *) == 8) {
		for (i = HANTRO_PP_EXT_FIRST_REG;
			i <= HANTRO_PP_EXT_LAST_REG; i++)
			iowrite32(dec_regs[id][i],
				(void *)(dev->hwregs[id] + i * 4));
	}
	/* write the stat reg, which may start the PP */
	iowrite32(dec_regs[id][HANTRO_PP_ORG_FIRST_REG],
		(void *)(dev->hwregs[id] + HANTRO_PP_ORG_FIRST_REG * 4));

	return 0;
}

long PPRefreshRegs(hantrodec_t *dev, struct core_desc *core)
{
	long i, ret;
	u32 id = core->id;

	if (sizeof(void *) == 8) {
		/* user has to know exactly what they are asking for */
		if (core->size != (HANTRO_PP_TOTAL_REGS * 4))
			return -EFAULT;
	} else {
		/* user has to know exactly what they are asking for */
		if (core->size != (HANTRO_PP_REGS * 4))
			return -EFAULT;
	}

	/* read all registers from hardware */
	/* both original and extended regs need to be read */
	for (i = HANTRO_PP_ORG_FIRST_REG; i <= HANTRO_PP_ORG_LAST_REG; i++)
		dec_regs[id][i] = ioread32((void *)(dev->hwregs[id] + i * 4));
	if (sizeof(void *) == 8) {
		for (i = HANTRO_PP_EXT_FIRST_REG;
			i <= HANTRO_PP_EXT_LAST_REG; i++)
			dec_regs[id][i] =
				ioread32((void *)(dev->hwregs[id] + i * 4));
	}
	/* put registers to user space */
	/* put original registers to user space */
	ret = copy_to_user(core->regs + HANTRO_PP_ORG_FIRST_REG,
			dec_regs[id] + HANTRO_PP_ORG_FIRST_REG,
			HANTRO_PP_REGS * 4);
	if (sizeof(void *) == 8) {
		/* put extended registers to user space*/
		ret = copy_to_user(core->regs + HANTRO_PP_EXT_FIRST_REG,
				dec_regs[id] + HANTRO_PP_EXT_FIRST_REG,
				HANTRO_PP_EXT_REGS * 4);
	}
	if (ret) {
		PRT_ERR("copy_to_user failed, returned %li\n", ret);
		return -EFAULT;
	}

	return 0;
}

static int CheckPPIrq(hantrodec_t *dev, int id)
{
	unsigned long flags;
	int rdy = 0;

	const u32 irq_mask = (1 << id);

	spin_lock_irqsave(&owner_lock, flags);

	if (pp_irq & irq_mask) {
		/* reset the wait condition(s) */
		pp_irq &= ~irq_mask;
		rdy = 1;
	}

	spin_unlock_irqrestore(&owner_lock, flags);

	return rdy;
}

long WaitPPReadyAndRefreshRegs(hantrodec_t *dev, struct core_desc *core)
{
	u32 id = core->id;

	PRT_ERR("wait_event_interruptible PP[%d]\n", id);

	if (wait_event_interruptible(pp_wait_queue, CheckPPIrq(dev, id))) {
		PRT_ERR("PP[%d]  wait_event_interruptible interrupted\n", id);
		return -ERESTARTSYS;
	}

	atomic_inc(&irq_tx);

	/* refresh registers */
	return PPRefreshRegs(dev, core);
}

static int CheckCoreIrq(hantrodec_t *dev, const struct file *filp, int *id)
{
	unsigned long flags;
	int rdy = 0, n = 0;

	do {
		u32 irq_mask = (1 << n);

		spin_lock_irqsave(&owner_lock, flags);

		if (dec_irq & irq_mask) {
			if (dec_owner[n] == filp) {
				/* we have an IRQ for our client */

				/* reset the wait condition(s) */
				dec_irq &= ~irq_mask;

				/* signal ready core no. for our client */
				*id = n;

				rdy = 1;

				spin_unlock_irqrestore(&owner_lock, flags);
				break;
			} else if (dec_owner[n] == NULL) {
				/* zombie IRQ */
				PRT_ERR("IRQ on core[%d], but no owner!!\n", n);

				/* reset the wait condition(s) */
				dec_irq &= ~irq_mask;
			}
		}

		spin_unlock_irqrestore(&owner_lock, flags);

		n++; /* next core */
	} while (n < dev->cores);

	return rdy;
}

long WaitCoreReady(hantrodec_t *dev, const struct file *filp, int *id)
{
	long ret;

	PRT_ERR("wait_event_interruptible CORE\n");
#ifdef USE_SW_TIMEOUT
	u32 i, status;

	ret = wait_event_interruptible_timeout(dec_wait_queue,
			CheckCoreIrq(dev, filp, id), msecs_to_jiffies(2000));
	if (ret < 0) {
		PRT_ERR("CORE  wait_event_interruptible interrupted\n");
		return -ERESTARTSYS;
	} else if (ret == 0) {
		PRT_ERR("CORE  wait_event_interruptible timeout\n");
		for (i = 0; i < dev->cores; i++) {
			status = ioread32((void *)(dev->hwregs[i] +
						HANTRODEC_IRQ_STAT_DEC_OFF));
			/* check if HW is enabled */
			if ((status & HANTRODEC_DEC_E) &&
					dec_owner[i] == filp) {
				PRT_ERR("CORE[%d] reset for timeout\n", i);
				*id = i;
				/* abort decoder */
				status |= HANTRODEC_DEC_ABORT |
						HANTRODEC_DEC_IRQ_DISABLE;
				iowrite32(status,
					(void *)(dev->hwregs[i] +
					HANTRODEC_IRQ_STAT_DEC_OFF));
				break;
			}
		}
	}
#else
	ret = wait_event_interruptible(dec_wait_queue,
					CheckCoreIrq(dev, filp, id));
	if (ret) {
		PRT_ERR("CORE  wait_event_interruptible interrupted\n");
		return -ERESTARTSYS;
	}
#endif
	atomic_inc(&irq_tx);

	return 0;
}


/*
 *Function name   : hantrodec_ioctl
 *Description     : communication method to/from the user space
 *
 *Return type     : long
 */

static long hantrodec_ioctl(struct file *filp, unsigned int cmd,
	unsigned long arg)
{
	int err = 0;
	long tmp;
#ifdef CLK_CFG
	unsigned long flags;
#endif

#ifdef HW_PERFORMANCE
	struct timeval *end_time_arg;
#endif

	//PRT_ERR("ioctl cmd 0x%08x\n", cmd);
	/*
	 * extract the type and number bitfields, and don't decode
	 * wrong cmds: return ENOTTY (inappropriate ioctl) before access_ok()
	 */
	if (_IOC_TYPE(cmd) != VDC_IOC_M)
		return -ENOTTY;
	if (_IOC_NR(cmd) > HANTRODEC_IOC_MAXNR)
		return -ENOTTY;

	/*
	 * the direction is a bitmask, and VERIFY_WRITE catches R/W
	 * transfers. `Type' is user-oriented, while
	 * access_ok is kernel-oriented, so the concept of "read" and
	 * "write" is reversed
	 */
	if (_IOC_DIR(cmd) & _IOC_READ)
		err = !access_ok(VERIFY_WRITE, (void *) arg, _IOC_SIZE(cmd));
	else if (_IOC_DIR(cmd) & _IOC_WRITE)
		err = !access_ok(VERIFY_READ, (void *) arg, _IOC_SIZE(cmd));

	if (err)
		return -EFAULT;

#ifdef CLK_CFG
	spin_lock_irqsave(&clk_lock, flags);
	if (clk_cfg != NULL && !IS_ERR(clk_cfg) && (is_clk_on == 0)) {
		PRT_ERR("turn on clock by user\n");
		if (clk_enable(clk_cfg)) {
			spin_unlock_irqrestore(&clk_lock, flags);
			return -EFAULT;
		}
		is_clk_on = 1;
	}
	spin_unlock_irqrestore(&clk_lock, flags);
	//mod_timer(&timer, jiffies + 10*HZ); /*the interval is 10s*/
#endif

	switch (cmd) {
	case HANTRODEC_IOC_CLI:
	{
		__u32 id;

		__get_user(id, (__u32 *)arg);
		if (id >= hantrodec_data.cores)
			return -EFAULT;

		disable_irq(hantrodec_data.irq[id]);
		break;
	}
	case HANTRODEC_IOC_STI:
	{
		__u32 id;

		__get_user(id, (__u32 *)arg);
		if (id >= hantrodec_data.cores)
			return -EFAULT;

		enable_irq(hantrodec_data.irq[id]);
		break;
	}
	case HANTRODEC_IOCGHWOFFSET:
	{
		__u32 id;

		__get_user(id, (__u32 *)arg);
		if (id >= hantrodec_data.cores)
			return -EFAULT;


		__put_user(multicorebase_actual[id], (unsigned long *) arg);
		break;
	}
	case HANTRODEC_IOCGHWIOSIZE:
	{
		__u32 id;
		__u32 io_size;

		__get_user(id, (__u32 *)arg);
		if (id >= hantrodec_data.cores)
			return -EFAULT;

		io_size = hantrodec_data.iosize[id];
		__put_user(io_size, (u32 *) arg);

		return 0;
	}
	case HANTRODEC_IOC_MC_OFFSETS:
	{
		tmp = copy_to_user((unsigned long *) arg, multicorebase_actual,
				sizeof(multicorebase_actual));
		if (err) {
			PRT_ERR("copy_to_user failed, returned %li\n", tmp);
			return -EFAULT;
		}
		break;
	}
	case HANTRODEC_IOC_MC_CORES:
		__put_user(hantrodec_data.cores, (unsigned int *) arg);
		PRT_ERR("hantrodec_data.cores=%d\n", hantrodec_data.cores);
		break;
	case HANTRODEC_IOCS_DEC_PUSH_REG:
	{
		struct core_desc core;

		/* get registers from user space*/
		tmp = copy_from_user(&core,
				(void *)arg, sizeof(struct core_desc));
		if (tmp) {
			PRT_ERR("copy_from_user failed, returned %li\n", tmp);
			return -EFAULT;
		}

		DecFlushRegs(&hantrodec_data, &core);
		break;
	}
	case HANTRODEC_IOCS_DEC_WRITE_REG:
	{
		struct core_desc core;

		/* get registers from user space*/
		tmp = copy_from_user(&core,
				(void *)arg, sizeof(struct core_desc));
		if (tmp) {
			PRT_ERR("copy_from_user failed, returned %li\n", tmp);
			return -EFAULT;
		}

		DecWriteRegs(&hantrodec_data, &core);
		break;
	}
	case HANTRODEC_IOCS_PP_PUSH_REG:
	{
		struct core_desc core;

		/* get registers from user space*/
		tmp = copy_from_user(&core,
				(void *)arg, sizeof(struct core_desc));
		if (tmp) {
			PRT_ERR("copy_from_user failed, returned %li\n", tmp);
			return -EFAULT;
		}

		PPFlushRegs(&hantrodec_data, &core);
		break;
	}
	case HANTRODEC_IOCS_DEC_PULL_REG:
	{
		struct core_desc core;

		/* get registers from user space*/
		tmp = copy_from_user(&core,
				(void *)arg, sizeof(struct core_desc));
		if (tmp) {
			PRT_ERR("copy_from_user failed, returned %li\n", tmp);
			return -EFAULT;
		}

		return DecRefreshRegs(&hantrodec_data, &core);
	}
	case HANTRODEC_IOCS_DEC_READ_REG:
	{
		struct core_desc core;

		/* get registers from user space*/
		tmp = copy_from_user(&core,
				(void *)arg, sizeof(struct core_desc));
		if (tmp) {
			PRT_ERR("copy_from_user failed, returned %li\n", tmp);
			return -EFAULT;
		}

		return DecReadRegs(&hantrodec_data, &core);
	}
	case HANTRODEC_IOCS_PP_PULL_REG:
	{
		struct core_desc core;

		/* get registers from user space*/
		tmp = copy_from_user(&core,
				(void *)arg, sizeof(struct core_desc));
		if (tmp) {
			PRT_ERR("copy_from_user failed, returned %li\n", tmp);
			return -EFAULT;
		}

		return PPRefreshRegs(&hantrodec_data, &core);
	}
	case HANTRODEC_IOCH_DEC_RESERVE:
	{
		PRT_ERR("Reserve DEC core, format = %li\n", arg);
		return ReserveDecoder(&hantrodec_data, filp, arg);
	}
	case HANTRODEC_IOCT_DEC_RELEASE:
	{
		if (arg >= hantrodec_data.cores || dec_owner[arg] != filp) {
			PRT_ERR("bogus DEC release, core = %li\n", arg);
			return -EFAULT;
		}

		PRT_ERR("Release DEC, core = %li\n", arg);

		ReleaseDecoder(&hantrodec_data, arg);

		break;
	}
	case HANTRODEC_IOCQ_PP_RESERVE:
		return ReservePostProcessor(&hantrodec_data, filp);
	case HANTRODEC_IOCT_PP_RELEASE:
	{
		if (arg != 0 || pp_owner[arg] != filp) {
			PRT_ERR("bogus PP release %li\n", arg);
			return -EFAULT;
		}

		ReleasePostProcessor(&hantrodec_data, arg);

		break;
	}
	case HANTRODEC_IOCX_DEC_WAIT:
	{
		struct core_desc core;

		/* get registers from user space */
		tmp = copy_from_user(&core,
				(void *)arg, sizeof(struct core_desc));
		if (tmp) {
			PRT_ERR("copy_from_user failed, returned %li\n", tmp);
			return -EFAULT;
		}

		return WaitDecReadyAndRefreshRegs(&hantrodec_data, &core);
	}
	case HANTRODEC_IOCX_PP_WAIT:
	{
		struct core_desc core;

		/* get registers from user space */
		tmp = copy_from_user(&core,
				(void *)arg, sizeof(struct core_desc));
		if (tmp) {
			PRT_ERR("copy_from_user failed, returned %li\n", tmp);
			return -EFAULT;
		}

		return WaitPPReadyAndRefreshRegs(&hantrodec_data, &core);
	}
	case HANTRODEC_IOCG_CORE_WAIT:
	{
		int id;

		tmp = WaitCoreReady(&hantrodec_data, filp, &id);
		__put_user(id, (int *) arg);
		return tmp;
	}
	case HANTRODEC_IOX_ASIC_ID:
	{
		u32 id;

		__get_user(id, (u32 *)arg);
		if (id >= hantrodec_data.cores)
			return -EFAULT;

		id = ioread32((void *)(hantrodec_data.hwregs[id]));
		__put_user(id, (u32 *) arg);
		return 0;
	}
	case HANTRODEC_IOCG_CORE_ID:
	{
		PRT_ERR("Get DEC Core_id, format = %li\n", arg);
		return GetDecCoreID(&hantrodec_data, filp, arg);
	}
	case HANTRODEC_IOX_ASIC_BUILD_ID:
	{
		u32 id, hw_id;

		__get_user(id, (u32 *)arg);
		if (id >= hantrodec_data.cores)
			return -EFAULT;

		hw_id = ioread32((void *)(hantrodec_data.hwregs[id]));
		PRT_ERR(" -- hw_id1 : %x\n", hw_id);
		if (IS_G1(hw_id >> 16) || IS_G2(hw_id >> 16))
			__put_user(hw_id, (u32 *) arg);
		else {
			hw_id = ioread32((void *)(hantrodec_data.hwregs[id]
						+ HANTRODEC_HW_BUILD_ID_OFF));
			PRT_ERR("hw_id : %x\n", hw_id);
			__put_user(hw_id, (u32 *) arg);
		}
		return 0;
	}
	case HANTRODEC_DEBUG_STATUS:
	{
		PRT_ERR("hantrodec: dec_irq = 0x%08x\n", dec_irq);
		PRT_ERR("hantrodec: pp_irq = 0x%08x\n", pp_irq);

		PRT_ERR("hantrodec: IRQs received/sent2user = %d/%d\n",
			atomic_read(&irq_rx), atomic_read(&irq_tx));

		for (tmp = 0; tmp < hantrodec_data.cores; tmp++) {
			PRT_ERR("hantrodec: dec_core[%li] %s\n",
					tmp, dec_owner[tmp] == NULL ?
							"FREE" : "RESERVED");
			PRT_ERR("hantrodec: pp_core[%li] %s\n",
				tmp, pp_owner[tmp] == NULL ?
						"FREE" : "RESERVED");
		}
		break;
	}
	default:
		return -ENOTTY;
	}

	return 0;
}

/*
 *Function name   : hantrodec_open
 *Description     : open method
 *
 *Return type     : int
 */

static int hantrodec_open(struct inode *inode, struct file *filp)
{
	PRT_ERR("dev opened\n");
	return 0;
}

/*
 *Function name   : hantrodec_release
 *Description     : Release driver
 *
 *Return type     : int
 */

static int hantrodec_release(struct inode *inode, struct file *filp)
{
	int n;
	hantrodec_t *dev = &hantrodec_data;

	PRT_ERR("closing ...\n");

	for (n = 0; n < dev->cores; n++) {
		if (dec_owner[n] == filp) {
			PRT_ERR("releasing dec core %i lock\n", n);
			ReleaseDecoder(dev, n);
		}
	}

	for (n = 0; n < 1; n++) {
		if (pp_owner[n] == filp) {
			PRT_ERR("releasing pp core %i lock\n", n);
			ReleasePostProcessor(dev, n);
		}
	}

	PRT_ERR("closed\n");
	return 0;
}

static void vdc_waitforidle(void)
{
	while (1) {
		if (vdc_is_idle == 1)
			break;

		usleep_range(20, 40);
		PRT_ERR("vdc is busy\n");

	}
}

static void vdc_clk_enable(void)
{
	int ret = 0;

	if (is_clk_on != 0) {
		PRT_ERR("clk is enabled, return :%d\n", is_clk_on);
		goto vdc_clk_enable_end;
	}
	/*enable clk*/
	PRT_ERR("set vdc clock to displaypll\n");
	if (clk_set_parent(clk_cfg, parent_pll))
		PRT_ERR("failed to set vdc parent to display_pll\n");

	ret = clk_set_rate(clk_cfg, VDC_CLK_RATE);
	if (ret)
		PRT_ERR("set vdc_clk rate err\n");

	clk_prepare_enable(clk_cfg);

vdc_clk_enable_end:
	is_clk_on = 1;
}

static void vdc_clk_disable(void)
{
	if (is_clk_on != 1) {
		PRT_ERR("clk is disabled, return : %d\n", is_clk_on);
		goto vdc_clk_disable_end;
	}

	vdc_waitforidle();
	/*disable clk*/
	clk_disable_unprepare(clk_cfg);

vdc_clk_disable_end:
	is_clk_on = 0;
	/*power off*/
}

int vdc_irq = -1;

static int asoc_vdc_probe(struct platform_device *pdev)
{
	int result, i;

	PRT_ERR("hantrodec: dec/pp kernel module\n");

	/* If base_port is set, use that for single core legacy mode */
	hantrodec_data.cores = 0;

	for (i = 0; i < HXDEC_MAX_CORES; i++) {
		hantrodec_data.hwregs[i] = 0;
		/* If user gave less core bases that we have by default,
		 * invalidate default bases
		 */
		if (elements && i >= elements)
			multicorebase[i] = -1;

		hantrodec_data.iosize[i] = -1;
		hantrodec_data.irq[i] = -1;
	}

	hantrodec_data.iosize[0] = DEC_IO_SIZE_0;
	hantrodec_data.irq[0] = irq[0];

	hantrodec_data.async_queue_dec = NULL;
	hantrodec_data.async_queue_pp = NULL;

	dec_irq = 0;
	pp_irq = 0;

#ifdef CLK_CFG
	/* first get clk instance pointer */
	vdc_ahb_gate = devm_clk_get(&pdev->dev, "vdc_ahb_gate");
	if (IS_ERR(vdc_ahb_gate)) {
		PRT_ERR("get vdc_ahb_gate clk failed!\n");
		goto err;
	}

	vdc_ahb_reset = devm_clk_get(&pdev->dev, "vdc_ahb_reset");
	if (IS_ERR(vdc_ahb_reset)) {
		PRT_ERR("getvdc_ahb_reset clk failed!\n");
		goto err;
	}

	vdc_axi_gate = devm_clk_get(&pdev->dev, "vdc_axi_gate");
	if (IS_ERR(vdc_axi_gate)) {
		PRT_ERR("get vdc_axi_gate clk failed!\n");
		goto err;
	}

	clk_cfg = devm_clk_get(&pdev->dev, "vdc_module_clk");
	if (IS_ERR(clk_cfg)) {
		PRT_ERR("get handrodec clk failed!\n");
		goto err;
	}

	parent_pll = clk_get(NULL, "vc_pll");
	if (!parent_pll || IS_ERR(parent_pll)) {
		PRT_ERR("get parent_pll clk failed!\n");
		goto err;
	}

	clk_prepare_enable(vdc_ahb_gate);
	clk_prepare_enable(vdc_ahb_reset);
	clk_prepare_enable(vdc_axi_gate);

	vdc_clk_enable();
#endif

	result = ReserveIO(pdev);
	if (result < 0)
		goto err;

	memset(dec_owner, 0, sizeof(dec_owner));
	memset(pp_owner, 0, sizeof(pp_owner));

	sema_init(&dec_core_sem, hantrodec_data.cores);
	sema_init(&pp_core_sem, 1);

	/* read configuration fo all cores */
	ReadCoreConfig(&hantrodec_data);

	/* reset hardware */
	ResetAsic(&hantrodec_data);

	vdc_irq = platform_get_irq(pdev, 0);
	if (vdc_irq < 0)
		PRT_ERR("irq get error\n");


	result = request_irq(vdc_irq, (void *)hantrodec_isr, 0, "hantrodec",
			(void *) &hantrodec_data);
	if (result)
		PRT_ERR("vc : register vc irq failed!\n");


	//enable_irq(vdc_irq);

	PRT_ERR("vdc_irq : %d -- %d\n", vdc_irq, hantrodec_data.irq[0]);

	hantrodec_data.irq[0] = vdc_irq;


	return 0;
err:
	ReleaseIO();
	PRT_ERR("hantrodec: module not inserted\n");
	return result;
}

static int vdc_remove(struct platform_device *pdev)
{
	hantrodec_t *dev = &hantrodec_data;
	int n = 0;
	/* reset hardware */
	ResetAsic(dev);

	/* free the IRQ */
	for (n = 0; n < dev->cores; n++) {
		if (dev->irq[n] != -1) {
			//disable_irq(dev->irq[n]);
			free_irq(dev->irq[n], (void *) dev);
		}
	}

	ReleaseIO();

#ifdef CLK_CFG
	if (clk_cfg != NULL && !IS_ERR(clk_cfg)) {
		vdc_clk_disable();
		//clk_disable_unprepare(clk_cfg);
		//is_clk_on = 0;
		PRT_ERR("turned off hantrodec clk\n");
	}

	clk_disable_unprepare(vdc_ahb_reset);
	clk_disable_unprepare(vdc_axi_gate);
	clk_disable_unprepare(vdc_ahb_gate);

	/*delete timer*/
	//del_timer(&timer);
#endif

	//unregister_chrdev(hantrodec_major, "hantrodec");

	PRT_ERR("hantrodec: module removed\n");
	return 0;
}

#ifdef CONFIG_PM
static int vdc_suspend(struct device *_dev)
{
	/* todo: Do other things about your module's suspend */
	return 0;
}

static int vdc_resume(struct device *_dev)
{
	/* todo: Do other things about your module's suspend */
	return 0;
}

static const struct dev_pm_ops vdc_pmops = {
	.suspend = vdc_suspend,
	.resume = vdc_resume,
};

#endif


static const struct file_operations vdc_fops = {
	.owner = THIS_MODULE,
	.open = hantrodec_open,
	.release = hantrodec_release,
	.unlocked_ioctl = hantrodec_ioctl,
	.fasync = NULL
};


static struct platform_driver vdc_platform_driver = {
	.driver = {
		.name = DEVDRV_NAME_VDC,
		.owner = THIS_MODULE,
		.of_match_table = vdc_of_match,
#ifdef CONFIG_PM
		.pm = &vdc_pmops,
#endif
	},
	.probe = asoc_vdc_probe,
	.remove = vdc_remove,
};


static struct miscdevice vdc_miscdevice = {
	.minor = VDC_DRV_MINOR,
	.name = DEVDRV_NAME_VDC,
	.fops = &vdc_fops,
};


/*
 *Function name   : hantrodec_init
 *Description     : Initialize the driver
 *
 *Return type     : int
 */
int __init hantrodec_init(void)
{
	int ret;

	PRT_ERR("#### vdc init !\n");

	ret = misc_register(&vdc_miscdevice);
	if (ret) {
		PRT_ERR("register vdc misc device failed!\n");
		goto err0;
	}

	ret = platform_driver_register(&vdc_platform_driver);
	if (ret) {
		PRT_ERR("register vdc platform driver pm error!\n");
		goto err1;
	}

	return 0;
err1:
	misc_deregister(&vdc_miscdevice);
err0:
	return ret;
}

/*
 *Function name   : hantrodec_cleanup
 *Description     : clean up
 *
 *Return type     : int
 */

void __exit hantrodec_cleanup(void)
{
	PRT_ERR("#### vdc exit !\n");

	platform_driver_unregister(&vdc_platform_driver);
	misc_deregister(&vdc_miscdevice);
}

/*
 *Function name   : CheckHwId
 *Return type     : int
 */
static int CheckHwId(hantrodec_t *dev)
{
	unsigned int hwid;
	int i;
	int found;
	unsigned int num_hw;

	found = 0;
	num_hw = sizeof(DecHwId) / sizeof(*DecHwId);

	for (i = 0; i < dev->cores; i++) {
		if (dev->hwregs[i] != NULL) {
			hwid = readl(dev->hwregs[i]);
			PRT_ERR("hantrodec: core %d HW ID=0x%x\n", i, hwid);
			hwid = (hwid >> 16) & 0xFFFF; /* product version only */

			while (num_hw--) {
				if (hwid == DecHwId[num_hw]) {
					PRT_ERR("vdec: Supported 0x%16lx\n",
						multicorebase_actual[i]);
					found++;
					dev->hw_id[i] = hwid;
					break;
				}
			}
			if (!found) {
				PRT_ERR("hantrodec: Unknown HW  at 0x%16lx\n",
					multicorebase_actual[i]);
				return 0;
			}
			found = 0;
			num_hw = sizeof(DecHwId) / sizeof(*DecHwId);
		}
	}

	return 1;
}

/*
 *Function name   : ReserveIO
 *Description     : IO reserve
 *
 *Return type     : int
 */
static int ReserveIO(struct platform_device *pdev)
{
	int i = 0;
	unsigned int hwid;
	u32 reg;
	struct resource *iores;

	memcpy(multicorebase_actual, multicorebase,
			HXDEC_MAX_CORES * sizeof(unsigned long));
	memcpy((unsigned int *)(hantrodec_data.iosize), iosize,
			HXDEC_MAX_CORES * sizeof(unsigned int));
	memcpy((unsigned int *)(hantrodec_data.irq),
			irq, HXDEC_MAX_CORES * sizeof(int));

	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!iores)
		PRT_ERR("platform_get_resource error\n");


	multicorebase_actual[0] = (unsigned long)iores->start;

	PRT_ERR("start : 0x%16llx\n", iores->start);

	if (1) {
		hantrodec_data.hwregs[i] = devm_ioremap_resource(&pdev->dev,
					iores);
		if (hantrodec_data.hwregs[i] == NULL) {
			PRT_ERR("hantrodec: failed to ioremap HW regs\n");
			ReleaseIO();
			return -EBUSY;
		}
		hantrodec_data.cores++;
		config.its_main_core_id[i] = -1;
		config.its_aux_core_id[i] = -1;

		/* product version only */
		hwid = ((readl(hantrodec_data.hwregs[i])) >> 16) & 0xFFFF;

		PRT_ERR("hwid : %x --- %x", hwid,
				(readl(hantrodec_data.hwregs[i])));

		if (IS_VC8000D(hwid)) {
			reg = readl(hantrodec_data.hwregs[i] +
						HANTRODEC_SYNTH_CFG_2_OFF);
			PRT_ERR("reg : %x\n", reg);

			if (((reg >> DWL_H264_PIPELINE_E) & 0x01U)
				|| ((reg >> DWL_JPEG_PIPELINE_E) & 0x01U)) {
				PRT_ERR("vdc need debug %d\n", __LINE__);
			}
		}
	}

	PRT_ERR(" hantrodec_data.cores : %d\n",  hantrodec_data.cores);

	/* check for correct HW */
	if (!CheckHwId(&hantrodec_data)) {
		ReleaseIO();
		return -EBUSY;
	}

	return 0;
}

/*
 *Function name   : releaseIO
 *Description     : release
 *
 *Return type     : void
 */

static void ReleaseIO(void)
{
	int i;

	for (i = 0; i < hantrodec_data.cores; i++)
		;
}

/*
 *Function name   : hantrodec_isr
 *Description     : interrupt handler
 *
 *Return type     : irqreturn_t
 */

irqreturn_t hantrodec_isr(int irq, void *dev_id)
{
	unsigned long flags;
	unsigned int handled;
	int i;
	u8 *hwregs;
	hantrodec_t *dev;
	u32 irq_status_dec;

	handled = 0;
	dev = (hantrodec_t *) dev_id;

	// PRT_ERR(" --- isr happened\n");
	spin_lock_irqsave(&owner_lock, flags);

	for (i = 0; i < dev->cores; i++) {
		u8 *hwregs = dev->hwregs[i];

		/* interrupt status register read */
		irq_status_dec = ioread32((void *)(hwregs +
						HANTRODEC_IRQ_STAT_DEC_OFF));

		if (irq_status_dec & HANTRODEC_DEC_IRQ) {
			/* clear dec IRQ */
			irq_status_dec &= (~HANTRODEC_DEC_IRQ);
			iowrite32(irq_status_dec, (void *)(hwregs +
						HANTRODEC_IRQ_STAT_DEC_OFF));

			//PRT_ERR("decoder IRQ received! core %d\n", i);

			atomic_inc(&irq_rx);

			dec_irq |= (1 << i);

			wake_up_interruptible_all(&dec_wait_queue);
			handled++;
		}
	}

	spin_unlock_irqrestore(&owner_lock, flags);

	if (!handled)
		PRT_ERR("IRQ received, but not hantrodec's!\n");


	(void)hwregs;
	return IRQ_RETVAL(handled);
}

/*
 *Function name   : ResetAsic
 *Description     : reset asic
 *
 *Return type     :
 */
void ResetAsic(hantrodec_t *dev)
{
	int i, j;
	u32 status;

	for (j = 0; j < dev->cores; j++) {
		status = ioread32((void *)(dev->hwregs[j] +
					HANTRODEC_IRQ_STAT_DEC_OFF));

		if (status & HANTRODEC_DEC_E) {
			/* abort with IRQ disabled */
			status = HANTRODEC_DEC_ABORT |
						HANTRODEC_DEC_IRQ_DISABLE;
			iowrite32(status, (void *)(dev->hwregs[j] +
						HANTRODEC_IRQ_STAT_DEC_OFF));
		}

		if (IS_G1(dev->hw_id[j]))
			/* reset PP */
			iowrite32(0, (void *)(dev->hwregs[j] +
				HANTRO_IRQ_STAT_PP_OFF));

		for (i = 4; i < dev->iosize[j]; i += 4)
			iowrite32(0, (void *)(dev->hwregs[j] + i));

	}
}

/*
 *Function name   : dump_regs
 *Description     : Dump registers
 *
 *Return type     :
 */
#ifdef HANTRODEC_DEBUG
void dump_regs(hantrodec_t *dev)
{
	int i, c;

	PRT_ERR("Reg Dump Start\n");
	for (c = 0; c < dev->cores; c++) {
		for (i = 0; i < dev->iosize[c]; i += 4 * 4) {
			PRT_ERR("\toffset %04X: %08X  %08X  %08X  %08X\n", i,
				ioread32(dev->hwregs[c] + i),
				ioread32(dev->hwregs[c] + i + 4),
				ioread32(dev->hwregs[c] + i + 16),
				ioread32(dev->hwregs[c] + i + 24));
		}
	}
	PRT_ERR("Reg Dump End\n");
}
#endif

module_init(hantrodec_init);
module_exit(hantrodec_cleanup);

/* module description */
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Verisilicon");
MODULE_DESCRIPTION("Driver module for Hantro Decoder/Post-Processor");

